package com.zipking.cloud.springbootmybatis.util.excel;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.zipking.cloud.springbootmybatis.persistance.entity.MsgLog;
import com.zipking.cloud.springbootmybatis.persistance.mapper.MsgLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
public class ExportListener<T> {

    private BaseMapper<T> baseMapper;
    private MsgLogMapper msgLogMapper;
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public ExportListener(BaseMapper<T> baseMapper) {
        this.baseMapper = baseMapper;
    }

    public ExportListener(MsgLogMapper orderMapper) {
        this.msgLogMapper = orderMapper;
    }

    public ExportListener(MsgLogMapper orderMapper, ThreadPoolTaskExecutor threadPoolTaskExecutor) {
        this.msgLogMapper = orderMapper;
        this.threadPoolTaskExecutor = threadPoolTaskExecutor;
    }

    private static final String DATA_FORMAT = "yyyy-MM-dd-HH-mm-ss";

    private static final String CHARACTER_UTF_8 = "UTF-8";

    private static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    private static final String CONTENT_DISPOSITION = "Content-Disposition";

    private static final String CACHE_CONTROL = "Cache-Control";

    private static final String NO_STORE = "no-store";

    private static final String MAX_AGE = "max-age=0";

    private static final int PAGE_SIZE = 10000;

    public void exportExcel(HttpServletResponse response, String sheetName, Class<T> pojoClass,
                            LambdaQueryWrapper queryWrapper) throws IOException {

        ServletOutputStream out = getServletOutputStream(response, sheetName);
        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        int totalCount = Math.toIntExact(msgLogMapper.selectCount(queryWrapper));
        int pageNumber = (int) Math.ceil((double) totalCount / (double) PAGE_SIZE);    //分页条数看情况

        // 去调用写入,根据数据库分页的总的页数来
        for (int i = 1; i <= pageNumber; i++) {
            //先定义一个空集合每次循环使他变成null减少内存的占用
            List<MsgLog> recordList = new ArrayList<>();
            Page<MsgLog> page = new Page<>(i, PAGE_SIZE);
            Page<MsgLog> pojoIPage = msgLogMapper.selectPage(page, queryWrapper);
            recordList = pojoIPage.getRecords();
            excelWriter.write(recordList, writeSheet);
            recordList.clear();
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
        out.flush();
    }

    public static ServletOutputStream getServletOutputStream(HttpServletResponse response, String sheetName) throws IOException {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATA_FORMAT);
        String nowTime = formatter.format(LocalDateTime.now());
        String fileName = sheetName.concat("_").concat(nowTime).concat(".xlsx");
        response.setContentType(CONTENT_TYPE);
        //设置字符集为utf-8
        response.setCharacterEncoding(CHARACTER_UTF_8);
        //用postman测正常，浏览器多了filename_=utf-8等字样
        response.setHeader(CONTENT_DISPOSITION,
                "attachment;filename=" + URLEncoder.encode(fileName, CHARACTER_UTF_8)
                        + ";filename*=utf-8''" + URLEncoder.encode(fileName, CHARACTER_UTF_8));
        //postman测会乱码，但浏览器下载就正常
//        response.setHeader(CONTENT_DISPOSITION,
//                "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        //发送一个报头，告诉浏览器当前页面不进行缓存，每次访问的时间必须从服务器上读取最新的数据
        return response.getOutputStream();
    }
    /**
     * 查询优化的方法
     */
    public void exportNoQueryCount(HttpServletResponse response, String sheetName, Class<T> pojoClass,
                                   LambdaQueryWrapper<T> queryWrapper) throws IOException {
        ServletOutputStream out = getServletOutputStream(response, sheetName);
        ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        int startIndex = 1;
        while (true) {
            int startParam = (startIndex - 1) * PAGE_SIZE;
            int pageNumber = (int) Math.ceil((double) startParam / (double) PAGE_SIZE + 1);
            Page<T> page = new Page<>(pageNumber, PAGE_SIZE, false);
            Page<T> pojoIPage = baseMapper.selectPage(page, queryWrapper);
            List<T> recordList = pojoIPage.getRecords();
            if (CollectionUtils.isEmpty(recordList)) {
                break;
            }
            startIndex++;
            excelWriter.write(recordList, writeSheet);
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }

    /**
     * @param response
     * @param sheetName
     * @param pojoClass
     */
    public void export8(HttpServletResponse response, String sheetName, Class<org.apache.poi.ss.formula.functions.T> pojoClass) {
        try {
            ServletOutputStream out = getServletOutputStream(response, sheetName);
            //这里指定需要表头，因为model通常包含表信头息
            ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();
            //写第一个sheet,数据全是List<String> 无模型映射关系
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();

            //分页查询数据
            int pageNumber = 1;
            int pageSize = 5000;
            int dataLength = pageSize;
            long start = System.currentTimeMillis() / 1000;//单位秒
            Map<String, Object> condition = new HashMap<>();
            List<MsgLog> resultList =  Lists.newArrayList();//置list为空，清空内存
            List<CompletableFuture<List<MsgLog>>> futureList = new ArrayList<>();
            while (dataLength == pageSize) {
                int startIndex = (pageNumber - 1) * pageSize;
                condition.put("pageNo", startIndex);
                condition.put("pageSize", pageSize);
                condition.put("routing_key", "XX");
                //resultList = orderMapper.queryOrderByPage(condition);
                CompletableFuture<List<MsgLog>> completableFutures = CompletableFuture
                        .supplyAsync(() ->this.msgLogMapper.selectListByWhereExt(condition), threadPoolTaskExecutor);
                futureList.add(completableFutures);
                resultList = completableFutures.get();
                if (org.springframework.util.CollectionUtils.isEmpty(resultList)) {
                    //写数据
                    excelWriter.write(resultList, writeSheet);
                    break;
                }
                dataLength = resultList.size();
                pageNumber++;
                //写数据
                excelWriter.write(resultList, writeSheet);
            }
            //关闭writer的输出流
            excelWriter.finish();
            long end = System.currentTimeMillis() / 1000;
            log.info("导出耗时：" + (end - start) + " 秒");
        } catch (Exception e) {
            log.error("导出异常", e);
        }
    }
}
